package io.github.wanggit.antrpc.console.service.impl;

import io.github.wanggit.antrpc.console.service.CallLogService;
import io.github.wanggit.antrpc.console.web.constants.WebConstants;
import io.github.wanggit.antrpc.console.web.dto.TimeRangeDTO;
import io.github.wanggit.antrpc.console.web.vo.CallLogPage;
import io.github.wanggit.antrpc.console.web.vo.CallLogsVO;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.metrics.ValueCount;
import org.elasticsearch.search.aggregations.metrics.ValueCountAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Component
public class CallLogServiceImpl implements CallLogService {

    @Autowired private RestHighLevelClient restHighLevelClient;

    @Override
    public CallLogPage query(String keyword, String range, int page) {
        long totalCount = getTotalCount(keyword, range);
        if (totalCount == 0) {
            return new CallLogPage();
        }
        CallLogPage callLogPage = new CallLogPage();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder(keyword, range);
        int size = 10;
        callLogPage.setPage(page);
        callLogPage.setTotalPage(
                totalCount % size == 0 ? totalCount / size : totalCount / size + 1);
        searchSourceBuilder
                .query(boolQueryBuilder)
                .sort("date", SortOrder.DESC)
                .from((page - 1) * size)
                .size(size);
        searchSourceBuilder.fetchSource(true);
        SearchRequest searchRequest = new SearchRequest(WebConstants.ELASTIC_CALL_LOGS_IDX);
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse searchResponse =
                    restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHit[] searchHits = searchResponse.getHits().getHits();
            List<CallLogsVO> callLogsVOList = new ArrayList<>();
            for (SearchHit hit : searchHits) {
                callLogsVOList.add(CallLogsVO.from(hit));
            }
            callLogPage.setCallLogsVOS(callLogsVOList);
            return callLogPage;
        } catch (ElasticsearchStatusException ese) {
            log.error(ese.getMessage(), ese);
        } catch (IOException e) {
            if (log.isErrorEnabled()) {
                log.error("query call logs error.", e);
            }
        }
        return new CallLogPage();
    }

    private long getTotalCount(String keyword, String range) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(getBoolQueryBuilder(keyword, range));
        ValueCountAggregationBuilder valueCountAggregationBuilder =
                AggregationBuilders.count("count").field("requestId.keyword");
        searchSourceBuilder.aggregation(valueCountAggregationBuilder);
        searchSourceBuilder.fetchSource(false);
        SearchRequest searchRequest = new SearchRequest(WebConstants.ELASTIC_CALL_LOGS_IDX);
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse searchResponse =
                    restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = searchResponse.getAggregations();
            ValueCount valueCount = aggregations.get("count");
            return valueCount.getValue();
        } catch (ElasticsearchStatusException ese) {
            log.error(ese.getMessage(), ese);
        } catch (IOException e) {
            if (log.isErrorEnabled()) {
                log.error("query total count error.", e);
            }
        }
        return 0;
    }

    private BoolQueryBuilder getBoolQueryBuilder(String keyword, String range) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.hasText(keyword)) {
            boolQueryBuilder.filter(
                    QueryBuilders.multiMatchQuery(keyword)
                            .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
                            .lenient(true));
        }
        TimeRangeDTO timeRangeDTO = TimeRangeDTO.buildOf(range);
        boolQueryBuilder.filter(
                QueryBuilders.rangeQuery("date")
                        .gte(timeRangeDTO.getStart())
                        .lt(timeRangeDTO.getEnd()));
        return boolQueryBuilder;
    }
}
